﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace ExpressionTreeLib.ViewModels
{
    [ExpressionType(typeof(LambdaExpression))]
    public class LambdaNodeViewModel : NodeViewModel
    {
        public string Body { get; private set; }
        public string ReturnType { get; private set; }

        public LambdaNodeViewModel()
        {
            this.Commands.Add(new DelegateCommand<object>(this.Evaluate) { Name = "Evaluate" });
            this.Commands.Add(new DelegateCommand<object>(this.FromText) { Name = "FromText" });
        }

        private void FromText(object obj)
        {
            Type resultType = ((LambdaExpression)this.Expression).ReturnType;
            ParameterExpression[] parameters = ((LambdaExpression)this.Expression).Parameters.ToArray();
            var body = new LongText(this.Body);

            if( !this.StepRequest(ref resultType, ref parameters, ref body))
                return;

            var exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(parameters, resultType, body.Text);

            this.ClearChildren();

            this.LoadExpressionNode(exp);
        }

        public override void LoadExpressionNode(Expression expression)
        {
            base.LoadExpressionNode(expression);

            var lambdaExpression = expression as LambdaExpression;
            this.Body = lambdaExpression.Body.ToString();
            this.ReturnType = lambdaExpression.ReturnType.Name;

            ExpressionNodeTypeMapping.Create(lambdaExpression.Body, this);

            foreach (var parameterExpression in lambdaExpression.Parameters)
            {
                ExpressionNodeTypeMapping.Create(parameterExpression, this);
            }
        }

        protected override Expression CreateExpression()
        {
            var body = this.Children[0].Expression;

            var parameters = new List<ParameterExpression>();

            foreach (var parameterViewModel in this.Children.OfType<ParameterViewModel>())
            {
                var parameterName = parameterViewModel.ParameterName;
                var parameterExpression = this.FindReferenceParameter(parameterName);
                parameters.Add(parameterExpression);
            }

            return Expression.Lambda(body, parameters);
        }
        
        private void Evaluate(object obj)
        {
            this.GetService<IEvaluateProvider>().Evaluate(this);
        }

        public override void GenerateCode(StringBuilder stringBuilder)
        {
            stringBuilder.Append("Expression.Lambda(");
            IndentStringBuilder.Current.IncreaseIndent(stringBuilder);

            this.Children[0].GenerateCode(stringBuilder);

            IndentStringBuilder.Current.StartNewLine(stringBuilder);

            foreach (var parameter in this.Children.Skip(1))
            {
                stringBuilder.Append(", ");
                parameter.GenerateCode(stringBuilder);
                IndentStringBuilder.Current.StartNewLine(stringBuilder);
            }

            stringBuilder.Append(")");
            IndentStringBuilder.Current.DecreaseIndent();
        }
    }
}
